// Copyright 2020 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>

#include "rkmedia_api.h"
#include "librtsp/rtsp_demo.h"

RK_CHAR *pDeviceName = "/dev/video25";
RK_U32 u32Width = 1280;
RK_U32 u32Height = 720;

static RK_CHAR optstr[] = "?::w:h:d:";
static const struct option long_options[] = {
    {"device_name", required_argument, NULL, 'd'},
    {"width", required_argument, NULL, 'w'},
    {"height", required_argument, NULL, 'h'},
    {"help", optional_argument, NULL, '?'},
    {NULL, 0, NULL, 0},
};

static void print_usage(const RK_CHAR *name) {
  printf("usage example:\n");
  printf("\t%s [-w 1280] "
         "[-h 720] "
         "[-d /dev/video25] \n",
         name);
  printf("\t-w | --width: UVC width, Default:1280\n");
  printf("\t-h | --heght: UVC height, Default:720\n");
  printf(
      "\t-d | --device_name: set device node(v4l2), Default:/dev/video25\n");
  printf("Notice: fmt always YUYV\n");
}


rtsp_demo_handle g_rtsplive = NULL;
static rtsp_session_handle g_rtsp_session;
static bool quit = false;
static void sigterm_handler(int sig) {
	fprintf(stderr, "signal %d\n", sig);
	quit = true;
}

void video_packet_cb(MEDIA_BUFFER mb) {
	static RK_S32 packet_cnt = 0;
	if (quit)
		return;

//	printf("#Get packet-%d, size %zu, %llu\n", packet_cnt, RK_MPI_MB_GetSize(mb), RK_MPI_MB_GetTimestamp(mb));

	if (g_rtsplive && g_rtsp_session) {
		rtsp_tx_video(g_rtsp_session, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), RK_MPI_MB_GetTimestamp(mb));
		rtsp_do_event(g_rtsplive);
	}

	RK_MPI_MB_ReleaseBuffer(mb);
	packet_cnt++;
}

int rtsp_enable(void)
{
	g_rtsplive = create_rtsp_demo(554);
	g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/main_stream");
//	rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
	rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H265, NULL, 0);
	rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
	return 0;
}

void rtsp_disable(void)
{
	rtsp_del_demo(g_rtsplive);
}

int pipeline_enable(void)
{
	int ret = 0;
	VMIX_DEV_INFO_S vmix_dev_info;
	VENC_CHN_ATTR_S venc_chn_attr;

	// create VI
	VI_CHN_ATTR_S vi_chn_attr;
	memset(&vi_chn_attr, 0, sizeof(vi_chn_attr));
	vi_chn_attr.pcVideoNode = pDeviceName;
	vi_chn_attr.u32BufCnt = 4;
	vi_chn_attr.u32Width = u32Width;
	vi_chn_attr.u32Height = u32Height;
	vi_chn_attr.enPixFmt = IMAGE_TYPE_YUYV422;
	vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
	vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;
	ret = RK_MPI_VI_SetChnAttr(0, 0, &vi_chn_attr);
	ret |= RK_MPI_VI_EnableChn(0, 0);
	if (ret) {
		printf("Create vi[%d] failed! ret=%d\n", 0, ret);
		return -1;
	}
	
	// create VENC
	memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
//	venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
//	venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
//	venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 25;
//	venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = RTSP_WIDTH * RTSP_HEIGHT;
//	venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
//	venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;
//	venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
//	venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;
    venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H265;
    venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H265CBR;
    venc_chn_attr.stRcAttr.stH265Cbr.u32Gop = 25;
    venc_chn_attr.stRcAttr.stH265Cbr.u32BitRate = u32Width * u32Height;
    venc_chn_attr.stRcAttr.stH265Cbr.fr32DstFrameRateDen = 1;
    venc_chn_attr.stRcAttr.stH265Cbr.fr32DstFrameRateNum = 25;
    venc_chn_attr.stRcAttr.stH265Cbr.u32SrcFrameRateDen = 1;
    venc_chn_attr.stRcAttr.stH265Cbr.u32SrcFrameRateNum = 25;
	venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_YUYV422;
	venc_chn_attr.stVencAttr.u32PicWidth = u32Width;
	venc_chn_attr.stVencAttr.u32PicHeight = u32Height;
	venc_chn_attr.stVencAttr.u32VirWidth = u32Width;
	venc_chn_attr.stVencAttr.u32VirHeight = u32Height;
	venc_chn_attr.stVencAttr.u32Profile = 77;
	ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
	if (ret) {
		printf("ERROR: create VENC[0] error! ret=%d\n", ret);
		return -1;
	}
	MPP_CHN_S stEncChn;
	stEncChn.enModId = RK_ID_VENC;
	stEncChn.s32DevId = 0;
	stEncChn.s32ChnId = 0;
	ret = RK_MPI_SYS_RegisterOutCb(&stEncChn, video_packet_cb);
	if (ret) {
		printf("ERROR: register output callback for VENC[0] error! ret=%d\n", ret);
		return -1;
	}
	
	// bind the pipeline
	MPP_CHN_S stSrcChn = {0};
	MPP_CHN_S stDestChn = {0};
	printf("#Bind VI[0] to VENC[0]....\n");
	stSrcChn.enModId = RK_ID_VI;
	stSrcChn.s32DevId = 0;
	stSrcChn.s32ChnId = 0; // invalid
	stDestChn.enModId = RK_ID_VENC;
	stDestChn.s32DevId = 0;
	stDestChn.s32ChnId = 0;
	ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
	if (ret) {
		printf("Bind VMX[0] to VENC[0] failed! ret=%d\n", ret);
		return -1;
	}
	
	return 0;
}

int pipeline_disable(void)
{
	int ret = 0;
	MPP_CHN_S stSrcChn = {0};
	MPP_CHN_S stDestChn = {0};
	
	printf("#UnBind VI[0] to VENC[0]....\n");
	stSrcChn.enModId = RK_ID_VI;
	stSrcChn.s32DevId = 0;
	stSrcChn.s32ChnId = 0; // invalid
	stDestChn.enModId = RK_ID_VENC;
	stDestChn.s32DevId = 0;
	stDestChn.s32ChnId = 0;
	ret = RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
	if (ret) {
		printf("UnBind VI[0] to VENC[0] failed! ret=%d\n", ret);
	}
	
	RK_MPI_VENC_DestroyChn(0);
	RK_MPI_VI_DisableChn(0, 0);

	return 0;
}

int main(int argc, char *argv[])
{
	int c;
	while ((c = getopt_long(argc, argv, optstr, long_options, NULL)) != -1) {
		const char *tmp_optarg = optarg;
		switch (c) {
			case 'w':
				u32Width = atoi(optarg);
				break;
			case 'h':
				u32Height = atoi(optarg);
				break;
			case 'd':
				pDeviceName = optarg;
				break;
			case '?':
			default:
				print_usage(argv[0]);
				return 0;
		}
	}	
	rtsp_enable();
	
	RK_MPI_SYS_Init();

	if (pipeline_enable()<0) {
		quit = true;
	}
	
	while (!quit) {
		usleep(500000);
	}
	
	pipeline_disable();
	rtsp_disable();
	
	return 0;
}
